package com.sparrow.socket.tcp.handler;

import com.sparrow.socket.tcp.session.impl.AbstractSession;
import com.sparrow.socket.tcp.session.impl.GameSession;
import com.sparrow.common.util.TimeUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

/**
 * 流量防火墙
 */
@ChannelHandler.Sharable
public class FlowFireHandler extends ChannelInboundHandlerAdapter {

    private static int SEC_BYTES_LIMIT = 8192;

    private static int SEC_PACKET_NUM_LIMIT = 128;

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        if (msg instanceof ByteBuf) {
            AbstractSession session = ctx.channel().attr(GameSession.SESSION_KEY).get();
            ByteBuf byteBuf = (ByteBuf) msg;
            FlowFireRecord fireRecord = session.getFlowFireRecord();
            if (fireRecord != null) {
                if (fireRecord.check(byteBuf)) {
                    ctx.channel().close();
                    byteBuf.release();
                    return;
                }
            }
        }
        ctx.fireChannelRead(msg);
    }


    public static class FlowFireRecord {
        private int timestamp;
        private int bytes;
        private int packets;

        public boolean check(ByteBuf byteBuf) {
            int now = TimeUtil.timestamp();
            if (this.timestamp != now) {
                this.timestamp = now;
                bytes = 0;
                packets = 0;
            }

            bytes += byteBuf.readableBytes();
            packets++;
            if (bytes >= SEC_BYTES_LIMIT) {
                return true;
            }
            if (packets >= SEC_PACKET_NUM_LIMIT) {
                return true;
            }
            return false;
        }
    }
}

